﻿using System;
using System.Globalization;

namespace MessagingWPF.Infrastructure.Utilities
{
	/// <summary>
	/// Simple helper class for argument checks.
	/// </summary>
	public static class Ensure
	{
		/// <summary>
		/// Helper method which throws an <see cref="ArgumentNullException" /> if the specified
		/// value is <c>null</c>.
		/// </summary>
		/// <typeparam name="T">The type of the argument.</typeparam>
		/// <param name="value">The value to check.</param>
		/// <param name="name">The name of the argument which is checked.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// Thrown when <paramref name="value"/> is <c>null</c>.
		/// </exception>
		public static void ArgumentIsNotNull<T>(T value, string name)
		{
			if (Equals(value, default(T)))
			{
				throw new ArgumentNullException(name);
			}
		}

		/// <summary>
		/// Helper method which throws an <see cref="ArgumentException" /> if the specified
		/// value is uninitialized (meaning holding the default value).
		/// </summary>
		/// <typeparam name="T">The type of the argument.</typeparam>
		/// <param name="value">The value to check.</param>
		/// <param name="name">The name of the argument which is checked.</param>
		/// <exception cref="T:System.ArgumentException">
		/// Thrown when <paramref name="value"/> is <c>null</c> for reference types, or zero for
		/// value types.
		/// </exception>
		public static void ArgumentIsNotDefault<T>(T value, string name) where T : struct 
		{
			if (Equals(value, default(T)))
			{
				if (!typeof(T).IsEnum)
				{
					throw new ArgumentException(name);
				}
			}
		}

		/// <summary>
		/// Checks a string argument to ensure it isn't null or empty.
		/// </summary>
		/// <param name="argumentValue">The argument value to check.</param>
		/// <param name="argumentName">The name of the argument.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// Thrown when <paramref name="argumentValue"/> is <c>null</c>.
		/// </exception>
		/// <exception cref="T:System.ArgumentException">
		/// Thrown when <paramref name="argumentValue"/> contains an empty 
		/// <see cref="string"/>.
		/// </exception>
		public static void ArgumentIsNotNullOrEmptyString(
			string argumentValue,
			string argumentName)
		{
			ArgumentIsNotNull(argumentValue, argumentName);

			if (string.IsNullOrEmpty(argumentValue))
			{
				throw new ArgumentException(argumentName);
			}
		}

		/// <summary>
		/// Throws an <see cref="ArgumentOutOfRangeException"/> when the specified value is not
		/// greater than the specified minimum.
		/// </summary>
		/// <typeparam name="T">Type of the value.</typeparam>
		/// <param name="value">The value to check.</param>
		/// <param name="minimum">The minimum.</param>
		/// <param name="name">The argument name.</param>
		public static void ArgumentIsGreater<T>(T value, T minimum, string name)
			where T : IComparable<T>
		{
			if (minimum.CompareTo(value) >= 0)
			{
				throw new ArgumentOutOfRangeException(
					name,
					value,
					string.Format(
						CultureInfo.InvariantCulture,
						"The argument must be greater than {0}.",
						minimum));
			}
		}

		/// <summary>
		/// Throws an <see cref="ArgumentException"/> when the specified type does not implement
		/// the specified interface.
		/// </summary>
		/// <typeparam name="TInterface">Interface the type must implement.</typeparam>
		/// <param name="type">The type to check.</param>
		/// <param name="name">The argument name.</param>
		public static void ArgumentImplements<TInterface>(Type type, string name)
		{
			if (!typeof(TInterface).IsAssignableFrom(type))
			{
				throw new ArgumentException(
					string.Format(
						CultureInfo.InvariantCulture,
						"Type {0} does not implement interface {1}.",
						type,
						typeof(TInterface)),
					name);
			}
		}
	}
}